package com.nantian.common.file;

import com.nantian.common.util.ClassUtils;
import com.nantian.constant.FileConsts;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.Map.Entry;


/**
 * <p>ClassName: PropertiesFactory.</p>
 * <p>Description: properties 工厂类</p>
 * 该类主要负责加载properties文件内容
 * <p>Copyright: Copyright©2014</p>
 * <p>Company: 广州南天电脑系统有限公司</p>
 * <p>Date: 2019-03-25</p>
 *
 * @author 刘晓辉
 * @version 1.0
 */
public class PropertiesFactory {
	private static Logger logger = LoggerFactory.getLogger(PropertiesFactory.class);
	/**
	 * Properties文件类型
	 */
	private static String PROPERTIES_TYPE = ".properties";
	/**
	 * 工厂类单例对象.
	 */
	private static PropertiesFactory propertiesFactory;
	/**
	 * properties实例集体合.<br>
	 * key：传入的properties文件路径<br>
	 * value：加载后的properties实体
	 */
	private volatile Map<String, PropertiesEntity> propertiesMap = new Hashtable<String, PropertiesEntity>();
	public static PropertiesFactory buildFactory() {
		synchronized (PropertiesFactory.class) {
			if (propertiesFactory == null) {
				propertiesFactory = new PropertiesFactory();
			}
		}
		return propertiesFactory;
	}

	public Map<String, PropertiesEntity> getPropertiesMap() {
		return propertiesMap;
	}

	/**
	 * 获取指定名称的properties文件.<br>
	 * 如果缓存中能找到该配置文件，则从缓存中获取<br>
	 * 否则，则从classes目录中加载properties文件，并将数据放在缓存中后返回该数据<br>
	 * 注：<br>
	 * 1.该properties必需放在classes目录下<br>
	 * 2.如果加载失败，则返回null对象<br>
	 * @param propertiesName properties文件名称
	 * @return PropertiesEntity实体对象
	 */
	public PropertiesEntity getPropertiesEntry(String propertiesName) {
		PropertiesEntity entry = propertiesMap.get(propertiesName);
		if (entry != null) {
			return entry;
		}
		synchronized (this) {
			if (!propertiesName.endsWith(PROPERTIES_TYPE)) {
				propertiesName = propertiesName + PROPERTIES_TYPE;
			}

			List<URL> urls = getConfigFilePaths(propertiesName);
			//获取最后一个，如果是文件夹部署，则优先文件夹，如果是war包部署，也支持war包
			String key = urls.get(urls.size() - 1).getPath();

			entry = propertiesMap.get(propertiesName);
			if (entry == null) {
				URL url = urls.get(urls.size() - 1);
				InputStream inputStream = null;
				Properties prop = new Properties();
				InputStreamReader reader = null;
				BufferedReader bf = null;
				try {
					inputStream = url.openStream();
					reader = new InputStreamReader(inputStream, CharEncoding.UTF_8);
					bf = new BufferedReader(reader);
					prop.load(bf);
					Map<Object, Object> data = new HashMap<Object, Object>(16);
					for (Entry<Object, Object> item : prop.entrySet()) {
						data.put(item.getKey(), item.getValue());
					}
					PropertiesEntity propertiesEntity = new PropertiesEntity(data);
					entry = propertiesEntity;
					propertiesMap.put(propertiesName, propertiesEntity);
				} catch (IOException e) {
					logger.error("加载properties文件失败：{}", key, e);
				} finally {
					IOUtils.closeQuietly(inputStream);
					IOUtils.closeQuietly(reader);
					IOUtils.closeQuietly(bf);
				}
			}
		}
		return entry;
	}

	private static List<URL> getConfigFilePaths(String name) {
		ClassLoader loader = ClassUtils.getClassLoader();
		Enumeration<URL> dirs = null;
		List<URL> paths = new ArrayList<URL>();
		try {
			dirs = loader.getResources(name);
		} catch (IOException e) {
			logger.error("加载配置文件{}失败", name);
			logger.error(e.getMessage(), e);
			return paths;
		}
		logger.debug("是否有{}文件：{}", name, dirs.hasMoreElements() );
		while (dirs.hasMoreElements()) {
			// 获取下一个元素
			URL url = dirs.nextElement();
			logger.debug("文件路径：{}", url.getPath());
			// 得到协议的名称
			String protocol = url.getProtocol();
			if (FileConsts.FILE_PROTOCOL.equals(protocol)) {
				paths.add(url);
			} else if (FileConsts.JAR_FILE.equals(protocol) || FileConsts.ZIP_FILE.equals(protocol)) {
				paths.add(0, url);
			}
		}
		return paths;
	}
}


